package com.enation.app.javashop.core.payment.service.impl;

import com.dag.eagleshop.core.account.model.dto.withdraw.MarkTransferredDTO;
import com.dag.eagleshop.core.account.model.enums.WithdrawStatusEnum;
import com.enation.app.javashop.core.member.model.dos.ConnectDO;
import com.enation.app.javashop.core.member.model.enums.ConnectTypeEnum;
import com.enation.app.javashop.core.member.service.ConnectManager;
import com.enation.app.javashop.core.member.service.MemberManager;
import com.enation.app.javashop.core.payment.model.dos.ThirdPartyTransferLogDO;
import com.enation.app.javashop.core.payment.model.enums.ClientType;
import com.enation.app.javashop.core.payment.model.enums.TransferCodeEnum;
import com.enation.app.javashop.core.payment.model.enums.WechatTransferStatusEnum;
import com.enation.app.javashop.core.payment.model.enums.WechatErrCodeEnum;
import com.enation.app.javashop.core.payment.model.vo.TransferBill;
import com.enation.app.javashop.core.payment.plugin.weixin.WeixinUtil;
import com.enation.app.javashop.core.payment.service.PaymentMethodManager;
import com.enation.app.javashop.core.payment.service.ThirdPartyTransferLogManager;
import com.enation.app.javashop.core.payment.service.WechatSmallchangeManager;
import com.enation.app.javashop.framework.context.AdminUserContext;
import com.enation.app.javashop.framework.exception.ServiceException;
import com.enation.app.javashop.framework.logs.Logger;
import com.enation.app.javashop.framework.logs.LoggerFactory;
import com.enation.app.javashop.framework.security.model.Admin;
import com.enation.app.javashop.framework.util.*;
import net.minidev.json.JSONObject;
import org.dom4j.Document;
import org.dom4j.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * WechatSmallchangeServiceImpl
 *
 * @author Chopper
 * @version v1.0
 * @since v7.0
 * 2019-06-11 下午4:26
 */
@Service
public class WechatSmallchangeManagerImpl implements WechatSmallchangeManager {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private PaymentMethodManager paymentMethodManager;
    @Autowired
    private ConnectManager connectManager;
    @Autowired
    private MemberManager memberManager;
    @Autowired
    private ThirdPartyTransferLogManager thirdPartyTransferLogManager;

    private final static String transferSnPrefix = "ZZ";
    /**
     * 支付url
     */
    private String PAY_BASE_URL = "https://api.mch.weixin.qq.com";

    /**
     * /**
     * <pre>
     * 企业付款业务是基于微信支付商户平台的资金管理能力，为了协助商户方便地实现企业向个人付款，针对部分有开发能力的商户，提供通过API完成企业付款的功能。
     * 比如目前的保险行业向客户退保、给付、理赔。
     * 企业付款将使用商户的可用余额，需确保可用余额充足。查看可用余额、充值、提现请登录商户平台“资金管理”https://pay.weixin.qq.com/进行操作。
     * 注意：与商户微信支付收款资金并非同一账户，需要单独充值。
     * 文档详见:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_2
     * 接口链接：https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers
     * </pre>
     * <p>
     * 自动发送零钱红包
     *
     * @param openId
     * @param price
     */
    @Override
    public boolean autoSend(String openId, Double price, String ip, String sn) {

        try {

            Map<String, String> returnParams = null;
            //2019-06-12 参数2 见 com.enation.app.javashop.core.payment.plugin.weixin.WeixinPuginConfig.getPluginId()
            Map<String, String> config = paymentMethodManager.getConfig(ClientType.WAP.getDbColumn(), "weixinPayPlugin");
            String appId = config.get("appid");
            String mchId = config.get("mchid");
            String key = config.get("key");
            String p12 = config.get("p12_path");

            File file = new File(p12);
            if (!file.exists()) {
                return false;
            }

            Map<String, String> params = new TreeMap<>();
            params.put("mch_appid", appId);
            params.put("mchid", mchId);
            //随机字符串，不长于32位
            params.put("nonce_str", StringUtil.getRandStr(10));
            params.put("openid", openId);
            params.put("partner_trade_no", sn);
            params.put("check_name", "NO_CHECK");
            params.put("amount", CurrencyUtil.toFen(price));
            params.put("desc", "佣金提现");
            params.put("spbill_create_ip", ip);

            String sign = WeixinUtil.createSign(params, key);
            //签名
            params.put("sign", sign);
            String url = PAY_BASE_URL + "/mmpaymkttransfers/promotion/transfers";

            String xml = null;
            try {
                xml = WeixinUtil.mapToXml(params);
            } catch (Exception e) {
                this.logger.error("不规范的参数:" + JsonUtil.objectToJson(params));
                throw new ServiceException("不规范的参数", e.getMessage());
            }
            logger.debug("url:" + url);
            logger.debug("params:" + xml);
            Document resultDoc = WeixinUtil.verifyCertPost(url, xml, mchId, p12);
            returnParams = WeixinUtil.xmlToMap(resultDoc);
            logger.debug("WechatMpService.entPay result:" + JsonUtil.objectToJson(returnParams));

            String errCode = returnParams.get("err_code");
            if (StringUtil.isEmpty(errCode)) {
                return true;
            }
            return false;
        } catch (Exception e) {
            this.logger.error("零钱接口异常", e);
            return false;
        }


    }

    /**
     * 转账到零钱
     *
     * @return
     */
    @Override
    public Map<String, String> transfer(TransferBill transferBill) {

        //转账日志
        ThirdPartyTransferLogDO transferLogDO = new ThirdPartyTransferLogDO();

        //转账单号
        String transferNo = transferBill.getPartnerTradeNo();
        //转账金额
        Double price = transferBill.getTransferPrice();
        //转账描述
        String describe = StringUtil.notEmpty(transferBill.getDescribe()) ? transferBill.getDescribe() : "智溢商城钱包提现";
        //收款人ID
        Integer payeeMemberId = transferBill.getPayeeMemberId();
        //真实姓名
        String realName = transferBill.getRealName();

        //返回的map
        Map<String, String> resultMap = new HashMap<>();
        resultMap.put("withdrawStatus", String.valueOf(WithdrawStatusEnum.PAY_UNDERWAY.getIndex())); //转账状态，默认是处理中
        resultMap.put("sendMessage", null); //给用户发送推送
        resultMap.put("alertMessage", null); //管理后台弹窗提示
        if (StringUtil.isEmpty(transferNo) || price == null || payeeMemberId == null || StringUtil.isEmpty(realName)) {
            resultMap.put("alertMessage", TransferCodeEnum.E610.getDescription());
            return resultMap;
        }

        transferLogDO.setTransferStatus(WithdrawStatusEnum.PAY_UNDERWAY.getName());
        transferLogDO.setTransferNo(transferNo);
        transferLogDO.setTransferPrice(price);
        transferLogDO.setTransferDescribe(describe);
        transferLogDO.setPayeeMemberId(payeeMemberId);
        transferLogDO.setPayeeMemberName(realName);

        //标识是否调用实际的转账
        Boolean status = false;
        //如果是正式环境，或测试环境实际转账金额是0.01，走实际的转账方法
        if (EnvironmentUtils.isProd()) {
            status = true;
        } else if (price == 0.01) {
            status = true;
        }

        if (status) {
            // 1、获取转账参数
            Map<String, String> config = paymentMethodManager.getConfig(ClientType.MINI.getDbColumn(), "weixinPayPlugin");
            if (config == null) {
                resultMap.put("alertMessage", TransferCodeEnum.E602.getDescription());
                return resultMap;
            }
            String appId = config.get("appid");
            String mchId = config.get("mchid");
            String key = config.get("key");
            String p12 = config.get("p12_path");
            File file = new File(p12);
            if (!file.exists()) {
                logger.error("找不到证书路径" + p12 + "，请联系管理员正确配置");
                resultMap.put("alertMessage", TransferCodeEnum.E604.getDescription());
                return resultMap;
            }

            //获取openID
            ConnectDO connectDO = connectManager.getConnect(payeeMemberId, ConnectTypeEnum.WECHAT.value());
            if (connectDO == null || StringUtil.isEmpty(connectDO.getOpenId())) {
                resultMap.put("alertMessage", TransferCodeEnum.E603.getDescription());
                return resultMap;
            }
            String openId = connectDO.getOpenId();
            transferLogDO.setOpenId(openId);

            // 2、组装请求报文
            Map params = new TreeMap();
            params.put("mch_appid", appId);
            params.put("mchid", mchId);
            params.put("nonce_str", StringUtil.getRandStr(10));
            params.put("partner_trade_no", transferNo);
            params.put("openid", openId);
            //校验真实姓名
            params.put("check_name", "FORCE_CHECK");
            params.put("re_user_name", realName);
            params.put("amount", CurrencyUtil.toFen(price));
            params.put("desc", describe);
            String sign = WeixinUtil.createSign(params, key);
            params.put("sign", sign);

            transferLogDO.setPayConfig(JSONObject.toJSONString(config));
            transferLogDO.setPaymentPluginId("weixinPayPlugin");
            transferLogDO.setCreateTime(DateUtil.getDateline());
            //操作人信息
            Admin admin = AdminUserContext.getAdmin();
            transferLogDO.setOperatorMemberId(admin.getUid());
            transferLogDO.setOperatorMemberName(admin.getUsername());

            try {
                String xml = WeixinUtil.mapToXml(params);
                logger.debug("发送到微信的报文：" + xml);
                transferLogDO.setRequestMsg(xml);

                // 3、发送请求，获取返回值
                Document resultDoc = WeixinUtil.verifyCertPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers", xml, mchId, p12);
                transferLogDO.setResultMsg(resultDoc.asXML());
                logger.debug("微信转账返回的报文：" + resultDoc.asXML());
                Element rootEl = resultDoc.getRootElement();
                // 返回结果
                String returnCode = rootEl.element("return_code").getText();
                if ("SUCCESS".equals(returnCode)) {
                    //表示发送请求成功
                    String resultCode = rootEl.element("result_code").getText();
                    if ("SUCCESS".equals(resultCode)) {
                        resultMap.put("sendMessage", "提现成功");
                        resultMap.put("alertMessage", TransferCodeEnum.E604.getDescription());
                        return resultMap;
                    } else {
                        //错误码
                        String errCode = rootEl.element("err_code").getText();
                        logger.error("微信提现到零钱失败,请联系管理员并提供提现错误信息：" + errCode + "," + rootEl.element("err_code_des").getText());
                        if (WechatErrCodeEnum.NAME_MISMATCH.value().equals(errCode)) {
                            resultMap.put("sendMessage", TransferCodeEnum.E611.getDescription());
                            resultMap.put("alertMessage", TransferCodeEnum.E611.getDescription());
                            return resultMap;
                        } else if (WechatErrCodeEnum.V2_ACCOUNT_SIMPLE_BAN.value().equals(errCode)) {
                            resultMap.put("sendMessage", TransferCodeEnum.E612.getDescription());
                            resultMap.put("alertMessage", TransferCodeEnum.E612.getDescription());
                            return resultMap;
                        } else {
                            resultMap.put("alertMessage", rootEl.element("err_code_des").getText());
                            return resultMap;
                        }
                    }
                } else {
                    Element returnMsg = rootEl.element("return_msg");
                    if (returnMsg != null) {
                        resultMap.put("alertMessage", returnMsg.getText());
                        return resultMap;
                    }
                    resultMap.put("alertMessage", TransferCodeEnum.E605.getDescription());
                    return resultMap;
                }
            } catch (Exception e) {
                this.logger.error("微信提现到零钱系统异常Exception:", e);
                resultMap.put("alertMessage", TransferCodeEnum.E609.getDescription());
                return resultMap;
            } finally {
                thirdPartyTransferLogManager.addThirdPartyTransferLog(transferLogDO);
            }
        } else {
            return resultMap;
        }
    }

    /**
     * 查询转账是否成功
     */
    @Override
    public Map<String, String> gettransferinfo(String transferNo) {
        //需要返回的提现单状态
        Map<String, String> resultMap = new HashMap<>();
        //转账状态
        resultMap.put("tranferStatus", String.valueOf(WithdrawStatusEnum.PAY_UNDERWAY.getIndex()));
        //失败的真正原因
        resultMap.put("failReason", null);

        // 1、获取转账参数
        Map<String, String> config = paymentMethodManager.getConfig(ClientType.MINI.getDbColumn(), "weixinPayPlugin");
        if (config == null) {
            throw new ServiceException(TransferCodeEnum.E602.value(), TransferCodeEnum.E602.getDescription());
        }

        String p12 = config.get("p12_path");
        File file = new File(p12);
        if (!file.exists()) {
            logger.error("找不到证书路径" + p12 + "，请联系管理员正确配置");
            throw new ServiceException(TransferCodeEnum.E604.value(), TransferCodeEnum.E604.getDescription());
        }

        String appId = config.get("appid");
        String mchId = config.get("mchid");
        String key = config.get("key");

        // 2、组装请求报文
        Map params = new TreeMap();
        params.put("appid", appId);
        params.put("mch_id", mchId);
        params.put("nonce_str", StringUtil.getRandStr(10));
        params.put("partner_trade_no", transferNo);
        String sign = WeixinUtil.createSign(params, key);
        params.put("sign", sign);

        try {
            String xml = WeixinUtil.mapToXml(params);
            logger.debug("查询转账结果时发送到微信的报文：" + xml);

            // 3、发送请求，获取返回值
            Document resultDoc = WeixinUtil.verifyCertPost("https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo", xml, mchId, p12);
            logger.debug("查询转账结果时微信返回的报文：" + resultDoc.asXML());
            Element rootEl = resultDoc.getRootElement();
            // 返回结果
            String returnCode = rootEl.element("return_code").getText();
            //微信的转账状态
            String transferStatus = "FAIL";
            if ("SUCCESS".equals(returnCode)) {
                //表示发送请求成功
                String resultCode = rootEl.element("result_code").getText();
                if ("SUCCESS".equals(resultCode)) {
                    //微信的转账状态
                    transferStatus = rootEl.element("status").getText();
                    if (WechatTransferStatusEnum.SUCCESS.value().equals(transferStatus)) {
                        //转账成功
                        resultMap.put("tranferStatus", String.valueOf(WithdrawStatusEnum.SUCCESS.getIndex()));
                    } else if (WechatTransferStatusEnum.FAIL.value().equals(transferStatus)) {
                        //转账失败
                        resultMap.put("tranferStatus", String.valueOf(WithdrawStatusEnum.FAIL.getIndex()));
                        resultMap.put("failReason", rootEl.element("reason").getText());
                    }
                } else {
                    //错误码
                    String errCode = rootEl.element("err_code").getText();
                    //如果是指定单号数据不存在，则表示没有转过帐，所以提现单状态应该是交易关闭，退还冻结金额
                    if (WechatErrCodeEnum.NOT_FOUND.value().equals(errCode)) {
                        resultMap.put("tranferStatus", String.valueOf(WithdrawStatusEnum.FAIL.getIndex()));
                        return resultMap;
                    }
                    String errCodeDes = rootEl.element("err_code_des").getText();
                    resultMap.put("failReason", errCodeDes);
                    logger.error("查询微信提现状态失败：" + resultDoc.toString());
                }
            } else {
                Element returnMsg = rootEl.element("return_msg");
                if (returnMsg != null) {
                    resultMap.put("failReason", returnMsg.getText());
                } else {

                    resultMap.put("failReason", TransferCodeEnum.E608.getDescription());
                }

            }
            
            //更新状态
            this.thirdPartyTransferLogManager.updateStatusByNo(transferNo, transferStatus);
            return resultMap;
        } catch (Exception e) {
            this.logger.error("查询微信提现状态失败", e);
        }
        return resultMap;

    }

}
